#include "s_oled_ssd1306.h"
#include <string.h>
// #include "s_fonts.h"

#if !S_OLED_SSD1306_USE_EXT_LIB

const unsigned char fonts_F6x8[][6] =       
{
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// sp
    0x00, 0x00, 0x00, 0x2f, 0x00, 0x00,// !
    0x00, 0x00, 0x07, 0x00, 0x07, 0x00,// "
    0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14,// #
    0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12,// $
    0x00, 0x62, 0x64, 0x08, 0x13, 0x23,// %
    0x00, 0x36, 0x49, 0x55, 0x22, 0x50,// &
    0x00, 0x00, 0x05, 0x03, 0x00, 0x00,// '
    0x00, 0x00, 0x1c, 0x22, 0x41, 0x00,// (
    0x00, 0x00, 0x41, 0x22, 0x1c, 0x00,// )
    0x00, 0x14, 0x08, 0x3E, 0x08, 0x14,// *
    0x00, 0x08, 0x08, 0x3E, 0x08, 0x08,// +
    0x00, 0x00, 0x00, 0xA0, 0x60, 0x00,// ,
    0x00, 0x08, 0x08, 0x08, 0x08, 0x08,// -
    0x00, 0x00, 0x60, 0x60, 0x00, 0x00,// .
    0x00, 0x20, 0x10, 0x08, 0x04, 0x02,// /
    0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
    0x00, 0x00, 0x42, 0x7F, 0x40, 0x00,// 1
    0x00, 0x42, 0x61, 0x51, 0x49, 0x46,// 2
    0x00, 0x21, 0x41, 0x45, 0x4B, 0x31,// 3
    0x00, 0x18, 0x14, 0x12, 0x7F, 0x10,// 4
    0x00, 0x27, 0x45, 0x45, 0x45, 0x39,// 5
    0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
    0x00, 0x01, 0x71, 0x09, 0x05, 0x03,// 7
    0x00, 0x36, 0x49, 0x49, 0x49, 0x36,// 8
    0x00, 0x06, 0x49, 0x49, 0x29, 0x1E,// 9
    0x00, 0x00, 0x36, 0x36, 0x00, 0x00,// :
    0x00, 0x00, 0x56, 0x36, 0x00, 0x00,// ;
    0x00, 0x08, 0x14, 0x22, 0x41, 0x00,// <
    0x00, 0x14, 0x14, 0x14, 0x14, 0x14,// =
    0x00, 0x00, 0x41, 0x22, 0x14, 0x08,// >
    0x00, 0x02, 0x01, 0x51, 0x09, 0x06,// ?
    0x00, 0x32, 0x49, 0x59, 0x51, 0x3E,// @
    0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C,// A
    0x00, 0x7F, 0x49, 0x49, 0x49, 0x36,// B
    0x00, 0x3E, 0x41, 0x41, 0x41, 0x22,// C
    0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C,// D
    0x00, 0x7F, 0x49, 0x49, 0x49, 0x41,// E
    0x00, 0x7F, 0x09, 0x09, 0x09, 0x01,// F
    0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A,// G
    0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F,// H
    0x00, 0x00, 0x41, 0x7F, 0x41, 0x00,// I
    0x00, 0x20, 0x40, 0x41, 0x3F, 0x01,// J
    0x00, 0x7F, 0x08, 0x14, 0x22, 0x41,// K
    0x00, 0x7F, 0x40, 0x40, 0x40, 0x40,// L
    0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F,// M
    0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F,// N
    0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E,// O
    0x00, 0x7F, 0x09, 0x09, 0x09, 0x06,// P
    0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
    0x00, 0x7F, 0x09, 0x19, 0x29, 0x46,// R
    0x00, 0x46, 0x49, 0x49, 0x49, 0x31,// S
    0x00, 0x01, 0x01, 0x7F, 0x01, 0x01,// T
    0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F,// U
    0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F,// V
    0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F,// W
    0x00, 0x63, 0x14, 0x08, 0x14, 0x63,// X
    0x00, 0x07, 0x08, 0x70, 0x08, 0x07,// Y
    0x00, 0x61, 0x51, 0x49, 0x45, 0x43,// Z
    0x00, 0x00, 0x7F, 0x41, 0x41, 0x00,// [
    0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55,// 55
    0x00, 0x00, 0x41, 0x41, 0x7F, 0x00,// ]
    0x00, 0x04, 0x02, 0x01, 0x02, 0x04,// ^
    0x00, 0x40, 0x40, 0x40, 0x40, 0x40,// _
    0x00, 0x00, 0x01, 0x02, 0x04, 0x00,// '
    0x00, 0x20, 0x54, 0x54, 0x54, 0x78,// a
    0x00, 0x7F, 0x48, 0x44, 0x44, 0x38,// b
    0x00, 0x38, 0x44, 0x44, 0x44, 0x20,// c
    0x00, 0x38, 0x44, 0x44, 0x48, 0x7F,// d
    0x00, 0x38, 0x54, 0x54, 0x54, 0x18,// e
    0x00, 0x08, 0x7E, 0x09, 0x01, 0x02,// f
    0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C,// g
    0x00, 0x7F, 0x08, 0x04, 0x04, 0x78,// h
    0x00, 0x00, 0x44, 0x7D, 0x40, 0x00,// i
    0x00, 0x40, 0x80, 0x84, 0x7D, 0x00,// j
    0x00, 0x7F, 0x10, 0x28, 0x44, 0x00,// k
    0x00, 0x00, 0x41, 0x7F, 0x40, 0x00,// l
    0x00, 0x7C, 0x04, 0x18, 0x04, 0x78,// m
    0x00, 0x7C, 0x08, 0x04, 0x04, 0x78,// n
    0x00, 0x38, 0x44, 0x44, 0x44, 0x38,// o
    0x00, 0xFC, 0x24, 0x24, 0x24, 0x18,// p
    0x00, 0x18, 0x24, 0x24, 0x18, 0xFC,// q
    0x00, 0x7C, 0x08, 0x04, 0x04, 0x08,// r
    0x00, 0x48, 0x54, 0x54, 0x54, 0x20,// s
    0x00, 0x04, 0x3F, 0x44, 0x40, 0x20,// t
    0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C,// u
    0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C,// v
    0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C,// w
    0x00, 0x44, 0x28, 0x10, 0x28, 0x44,// x
    0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C,// y
    0x00, 0x44, 0x64, 0x54, 0x4C, 0x44,// z
    0x14, 0x14, 0x14, 0x14, 0x14, 0x14,// horiz lines
};

#endif

#if !S_OLED_SSD1306_USE_EXT_LIB

static oled_ssd1306_state s_oled_ssd1306_set_position(s_oled_ssd1306_t *oled, unsigned char x, unsigned char y) 
{
    unsigned char cmd[3] = {0xB0 + y, ((x & 0xF0) >> 4) | 0x10, 0x0F & x};
    if(!oled->ssd1306_write) {
        return oled_write_not_config;
    }
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 3);
    return oled_ssd1306_ok;
}

oled_ssd1306_state s_oled_ssd1306_show_char(s_oled_ssd1306_t *oled, unsigned char x, unsigned char y, unsigned char c)
{
    if(!oled->ssd1306_write) {
        return oled_write_not_config;
    }
    if(x > oled->colunm - 1) {
        x = 0;
        y++;
    }
    s_oled_ssd1306_set_position(oled, x, y);
    oled->ssd1306_write(S_OLED_SSD1306_DATA_REG, &fonts_F6x8[c - ' '][0], 6);
    return oled_ssd1306_ok;
}


#endif

oled_ssd1306_state s_oled_ssd1306_clear(s_oled_ssd1306_t *oled)
{
    if(!oled->ssd1306_write) {
        return oled_write_not_config;
    }

    unsigned char cmd[3];
    unsigned char i;
    unsigned char clear_data[128];
    if(oled->invert_enable) {
        memset(clear_data, 0xFF, 128);
    } else {
        memset(clear_data, 0x00, 128);
    }
    
    cmd[1] = 0x00;
    cmd[2] = 0x10;
    for(i = 0; i < oled->row / 8; i++) {
        cmd[0] = 0xB0 + i;
        oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 3);
        oled->ssd1306_write(S_OLED_SSD1306_DATA_REG, clear_data, oled->colunm);
    }
    return oled_ssd1306_ok;
}


oled_ssd1306_state s_oled_ssd1306_init(s_oled_ssd1306_t *oled)
{
    unsigned char cmd[2];

    if(!oled->delay_ms) {
        return oled_delay_ms_not_config;
    }
    oled->delay_ms(100);
    if(!oled->ssd1306_write) {
        return oled_write_not_config;
    }
    cmd[0] = 0xAE;                 //turn off oled panel
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 1);

    cmd[0] = 0xD5;                 //set display clock divide ratio/oscillator frequency
    cmd[1] = 0x80;                 //set divide ratio, Set Clock as 100 Frames/Sec
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0x20;                 //Set Memory Addressing Mode (0x00 / 0x01 / 0x02)
    cmd[1] = 0x02;                 //Page Addressing
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0xA8;                 //set multiplex ratio (16 to 63)
    cmd[1] = oled->row - 1;        //
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0xDA;                 //set com pins hardware configuration
    cmd[1] = 0x02 | (oled->Alternative_COM_pin_Enable) << 4;                 //Sequential COM pin configuration，Enable COM Left/Right remap
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    // Direction setup
    cmd[0] = 0xA0 | oled->uptodown_enable;                 //Set SEG / Column Mapping, 0xA0(left to right, reset value), 0xA1 normal mode
    cmd[1] = 0xC0 | (oled->uptodown_enable) << 3;          // Set COM / Row Scan Direction   0xc0(up to down, reset value), 0xC8 normal mode
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 1);
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, &cmd[1], 1);
    // Direction setup end

    cmd[0] = 0x40;                 //Set Display Start Line (0x40~0x7F)
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 1);

    cmd[0] = 0xD3;                 //set display offset (0x00~0x3F)
    cmd[1] = 0x00;                 //not offset
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0x81;                 //set contrast control register (0x00~0x100)
    cmd[1] = 0x00; //0xCF;                 //Set SEG Output Current Brightness
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0xD9;                 //set pre-charge period
    cmd[1] = 0xF1;                 //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0xD8;                 //set vcomh (0x00 / 0x20 / 0x30)
    cmd[1] = 0x20;                 //Set VCOM Deselect Level
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0x8D;                 //set Charge Pump enable / disable (0x14 / 0x10)
    cmd[1] = 0x14;                 //Enable charge pump during display on
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);

    cmd[0] = 0xA4;                 //Entire Display On (0xA4 / 0xA5)
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 1);
    
    cmd[0] = 0xA6;                 //set normal display (0xA6 / 0xA7)
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 1);

    s_oled_ssd1306_clear(oled);

    cmd[0] = 0xAF;                 //turn on oled panel
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 1);

    return oled_ssd1306_ok;
}

void s_oled_ssd1306_para_init(s_oled_ssd1306_t *oled)
{
    oled->colunm = 128;
    oled->row = 64;
    oled->Alternative_COM_pin_Enable = 1;
    oled->uptodown_enable = 0;
    oled->invert_enable = 0;
    // oled->uptodown_state = 0;
}

oled_ssd1306_state s_oled_ssd1306_set_brightness(s_oled_ssd1306_t *oled, unsigned char br)
{
    unsigned char cmd[2] = {0x81, br};
    if(!oled->ssd1306_write) {
        return oled_write_not_config;
    }
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 2);
    return oled_ssd1306_ok;
}

#if S_OLED_SSD1306_USE_EXT_LIB

static oled_ssd1306_state s_oled_ssd1306_ext_basic_check(s_oled_ssd1306_t *oled)
{
    if(!oled) {
        return oled_ssd1306_ext_oled_t_null;
    }
    if(!oled->ssd1306_write) {
        return (oled_ssd1306_state)oled_write_not_config;
    }

    if(!oled->oled_buf) {
        return oled_ssd1306_ext_oled_buf_null;
    }

    return (oled_ssd1306_state)oled_ssd1306_ok;
}

oled_ssd1306_state s_oled_ssd1306_ext_buf_draw(s_oled_ssd1306_t *oled)
{
    unsigned char cmd[3], i;

    oled_ssd1306_state rslt = s_oled_ssd1306_ext_basic_check(oled);
    if(rslt) {
        return rslt;
    }

    cmd[1] = 0x00;
    cmd[2] = 0x10;
    for(i = 0; i < oled->row / 8; i++) {
        cmd[0] = 0xB0 + i;
        oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 3);
        oled->ssd1306_write(S_OLED_SSD1306_DATA_REG, oled->oled_buf[i], oled->colunm);
    }

    return (oled_ssd1306_state)oled_ssd1306_ok;
}

oled_ssd1306_state s_oled_ssd1306_ext_init(s_oled_ssd1306_t *oled)
{
    unsigned char i;
    if(!oled) {
        return oled_ssd1306_ext_oled_t_null;
    }

    if(!oled->oled_malloc) {
        return oled_ssd1306_ext_oled_t_malloc_not_config;
    }
    
    oled->oled_buf = (unsigned char **)oled->oled_malloc(sizeof(unsigned char *) * oled->row / 8);
    if(!oled->oled_buf) {
        return oled_ssd1306_ext_oled_t_malloc_failed;
    }

    for(i = 0; i < oled->row / 8; i++) {
        oled->oled_buf[i] = (unsigned char *)oled->oled_malloc(oled->colunm);
        if(!oled->oled_buf[i]) {
            // TODO free before
            return oled_ssd1306_ext_oled_t_malloc_failed;
        }
        if(oled->invert_enable) {
            memset(oled->oled_buf[i], 0xFF, oled->colunm);
        } else {
            memset(oled->oled_buf[i], 0x00, oled->colunm);
        }
        
    }

    return (oled_ssd1306_state)oled_ssd1306_ok;
}

oled_ssd1306_state s_oled_ssd1306_ext_uptodown_change(s_oled_ssd1306_t *oled)
{
    unsigned char cmd[2];
    oled_ssd1306_state rslt = s_oled_ssd1306_ext_basic_check(oled);
    if(rslt) {
        return rslt;
    }
    
    if(oled->uptodown_enable) {
        oled->uptodown_enable = 0U;
    } else {
        oled->uptodown_enable = 1U;
    }
    cmd[0] = 0xA0 | oled->uptodown_enable;                 //Set SEG / Column Mapping, 0xA0(left to right, reset value), 0xA1 normal mode
    cmd[1] = 0xC0 | (oled->uptodown_enable) << 3;          // Set COM / Row Scan Direction   0xc0(up to down, reset value), 0xC8 normal mode
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, cmd, 1);
    oled->ssd1306_write(S_OLED_SSD1306_CMD_REG, &cmd[1], 1);

    return (oled_ssd1306_state)oled_ssd1306_ok;
}

oled_ssd1306_state s_oled_ssd1306_ext_clear(s_oled_ssd1306_t *oled)
{
    unsigned char i;
    oled_ssd1306_state rslt = s_oled_ssd1306_ext_basic_check(oled);
    if(rslt) {
        return rslt;
    }
    for(i = 0; i < oled->row / 8; i++) {
        if(oled->invert_enable) {
            memset(oled->oled_buf[i], 0xFF, oled->colunm);
        } else {
            memset(oled->oled_buf[i], 0x00, oled->colunm);
        }
        
    }

    return (oled_ssd1306_state)oled_ssd1306_ok;
}

oled_ssd1306_state s_oled_ssd1306_ext_draw_point(s_oled_ssd1306_t *oled, unsigned char x, unsigned char y, unsigned char enable)
{
    unsigned char *p_data;

    if(x > oled->colunm - 1) {
        x = oled->colunm - 1;
    }
    if(y > oled->row - 1) {
        y = oled->row - 1;
    }

    // printf("%d-%d~%d\r\n", x, y, enable);

    p_data = oled->oled_buf[y / 8] + x;

    if((enable && !oled->invert_enable) || (!enable && oled->invert_enable)) {
        *p_data |= 1 << (y % 8);
    } else {
        *p_data &= ~(1 << (y % 8));
    }

    return (oled_ssd1306_state)oled_ssd1306_ok;

}

#endif



